package cn.tedu.csmall.passport.filter;

import cn.tedu.csmall.commons.pojo.po.AdminLoginInfoPO;
import cn.tedu.csmall.passport.cache.AdminJwtRepository;
import cn.tedu.csmall.passport.security.LoginPrincipal;
import cn.tedu.csmall.passport.web.JsonResult;
import cn.tedu.csmall.passport.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;

/**
 * JWT 过滤器，需要解决的问题：接受JWT，解析JWT，将解析得到的数据创建为认证信息并存入到SecurityContext
 */
@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Value("${csmall.jwt.secret-key}")
    private String secretKey;

    @Autowired
    private AdminJwtRepository adminJwtRepository;

    /**
     * JWT的最小长度值
     */
    public static final int JWT_MIN_LENGTH = 113;

    public JwtAuthorizationFilter() {
        log.debug("创建过滤器类对象：JwtAuthorizationFilter");
    }

    /**
     * 放行清单
     */
    public String[] permitUrlList = {
            "/admins/login"
    };

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.debug("JwtAuthorizationFilter过滤器开始执行……");

        // 对放行清单中的访问直接放行
        String requestURI = request.getRequestURI();
        for (String s : permitUrlList) {
            if (requestURI.equals(s)) {
                filterChain.doFilter(request, response);
                return;
            }
        }

        // 根据业内惯用做法，客户端会将JWT放在请求头（Request Header）中的Authorization 属性中
        String jwt = request.getHeader("Authorization");
        log.debug("客户端携带的JWT：{}", jwt);

        // 判断客户端是否携带了有效的JWT
        if (!StringUtils.hasText(jwt) || jwt.length() < JWT_MIN_LENGTH) {
            // 如果JWT无效，直接放行
            log.debug("客户端没有携带有效的JWT，将放行，由后续的过滤器等组件继续处理此请求……");
            filterChain.doFilter(request, response);
            return;
        }

        // 从Redis读取管理员登录信息
        AdminLoginInfoPO loginInfo = adminJwtRepository.getLoginInfo(jwt);
        log.debug("从Redis中读取管理员的登录信息：{}", loginInfo);

        //检查是否盗用
        String remoteAddr = request.getRemoteAddr();
        String userAgent = request.getHeader("User-Agent");
        if (loginInfo == null || (!remoteAddr.equals(loginInfo.getRemoteAddr()) && !userAgent.equals(loginInfo.getUserAgent()))) {
            log.warn("JWT可能被盗用，将不向SecurityContext中存入任何认证信息，当前过滤器直接放行");
            filterChain.doFilter(request, response);
            return;
        }

        // 检查账号状态
        if (loginInfo.getEnable() == 0) {
            log.warn("账号状态被禁用，将JWT禁用，且响应错误");
            String message = "访问错误，账号已经被禁用！";
            response.setContentType("application/json; charset=utf-8");
            PrintWriter writer = response.getWriter();
            JsonResult jsonResult = JsonResult.error(ServiceCode.ERR_UNAUTHORIZED_DISABLE, message);
            String jsonString = JSON.toJSONString(jsonResult);
            writer.println(jsonString);
            writer.close();
        }

        // 解析JWT过程中可能出现异常，需要处理
        // 尝试解析JWT
        Claims claims = null;
        response.setContentType("application/json; charset=utf-8");
        try {
            claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
        } catch (ExpiredJwtException e) {
            String message = "您的登陆信息过期，请重新登录！";
            log.warn("程序运行过程中出现ExpiredJwtException，将向客户端响应错误信息！");
            log.warn("错误信息：{}", message);

            JsonResult jsonResult = JsonResult.error(ServiceCode.ERR_JWT_EXPIRED, message);
            String jsonString = JSON.toJSONString(jsonResult);

            PrintWriter printWriter = response.getWriter();
            printWriter.println(jsonString);
            printWriter.close();
            return;
        } catch (MalformedJwtException e) {
            String message = "非法访问！";
            log.warn("程序运行过程中出现MalformedJwtException，将向客户端响应错误信息！");
            log.warn("错误信息：{}", message);

            JsonResult jsonResult = JsonResult.error(ServiceCode.ERR_JWT_MALFORMED, message);
            String jsonString = JSON.toJSONString(jsonResult);

            PrintWriter printWriter = response.getWriter();
            printWriter.println(jsonString);
            printWriter.close();
            return;
        } catch (SignatureException e) {
            String message = "非法访问！";
            log.warn("程序运行过程中出现SignatureException，将向客户端响应错误信息！");
            log.warn("错误信息：{}", message);

            JsonResult jsonResult = JsonResult.error(ServiceCode.ERR_JWT_SIGNATURE, message);
            String jsonString = JSON.toJSONString(jsonResult);

            PrintWriter printWriter = response.getWriter();
            printWriter.println(jsonString);
            printWriter.close();
            return;
        } catch (Throwable e) {
            String message = "服务器忙，请稍后再试！【在开发过程中，如果看到此提示，应该检查服务器端的控制台，分析异常，并在JWT过滤器中补充处理对应异常的代码块】";
            log.warn("异常：", e);

            JsonResult jsonResult = JsonResult.error(ServiceCode.ERR_UNKNOWN, message);
            String jsonString = JSON.toJSONString(jsonResult);

            PrintWriter printWriter = response.getWriter();
            printWriter.println(jsonString);
            printWriter.close();
            return;
        }

        // 获取JWT中的数据
        Long id  = claims.get("id", Long.class);
        String username = claims.get("username", String.class);
        // String authoritiesJsonString = claims.get("authoritiesJsonString", String.class);// 获取jwt中的 authoritiesJsonString


        // 创建当事人数据
        LoginPrincipal loginPrincipal = new LoginPrincipal()
                .setId(id)
                .setUsername(username);

        // 创建认证信息
        Object principal = loginPrincipal;// 可以是任何类型，暂时使用用户名
        Object credentials = null;// 本次不需要
        Collection<SimpleGrantedAuthority> authorities = JSON.parseArray(loginInfo.getAuthorityListJsonString(), SimpleGrantedAuthority.class);// 使用真实权限
        Authentication authentication = new UsernamePasswordAuthenticationToken(principal, credentials, authorities);

         // 将认证结果存入到SecurityContext中
         SecurityContext securityContext = SecurityContextHolder.getContext();
         securityContext.setAuthentication(authentication);

        // 放行
        filterChain.doFilter(request, response);
    }

}
